### Global Variables
set(j_empty "")
set(j_3rd_list "")

### General functions

# j_assert(msg [expr]) 
# expr can be any expression suitable for if() command. 
# note: can't pass "" as empty string to j_assert, it's a CMake limitation. use j_empty instead.
# if if(expr) is false, raise a FATAL_ERROR message
macro (j_assert msg)
	if(${ARGN})
		# do nothing
	else()
		message(FATAL_ERROR "Assertion Failed, Expr: ${ARGN} Msg: ${msg}")
	endif()
endmacro()

# convert path to cmake style absolute path and set to var

# convert path to cmake style absolute path and set the variable var with it
function (j_set_path var path)
	file(TO_CMAKE_PATH ${path} result)
	get_filename_component(result ${result} ABSOLUTE)
	set(${var} ${result} PARENT_SCOPE)
endfunction()

macro (j_define_global_variables)
	set(var_list 
		j_project_file 
		j_project_name 
		j_project_root 
		j_projects_dir
		j_output_root_dir
		j_project_output_root_dir
		j_sibling_root_dir
		j_toolset
		j_toolset_ver
		j_platform
		j_architecture
		j_config_dir
		j_generated_project_dir
		j_jenny_root
		j_jenny_tools
		j_jenny_3rd_cache
		j_jenny_3rd_libs

		j_cmd_sync_project
		j_cmd_sync_3rd
# the following will be init in config.cmake
		j_compiler
		j_compiler_version
		j_compiler_name
		j_pkg_type
		j_pkg_os
		j_pkg_arch
		j_pkg_config
		j_pkg_compiler
		j_pkg_extension
		j_pkg_server
		j_pkg_server_root
		j_pkg_local_root
		j_server_user_passwd
		)
	set(j_global_variables ${var_list})
	foreach(var IN LISTS var_list)
		set(${var} "${${var}}")
	endforeach()

	set(j_pkg_type "zip")
	set(j_config "${CMAKE_BUILD_TYPE}" )
	set(j_platform "${J_TARGET_PLATFORM}" )
	set(j_architecture "${J_ARCHITECTURE}" )
	set(j_toolset "${J_TOOLSET}" )
	set(j_toolset_ver "${J_TOOLSET_VER}" )
	set(j_projects_dir "build/projects")
	get_filename_component(j_jenny_root ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
	set(j_jenny_tools ${j_jenny_root}/tools)
	if (WIN32)
		set(j_cmd_sync_project ${j_jenny_tools}/gitsync.bat)
		set(j_cmd_sync_3rd ${j_jenny_tools}/3rdsync.bat)
	else()
		set(j_cmd_sync_project ${j_jenny_tools}/gitsync.sh)
		set(j_cmd_sync_3rd ${j_jenny_tools}/3rdsync.sh)
	endif()

	get_filename_component(j_pkg_local_root ${j_jenny_root} DIRECTORY)
	set(j_pkg_extension ".zip")
	set(j_pkg_server "")
	set(j_pkg_server_root "")
endmacro()

macro (j_set_global_variables)

	get_filename_component(config_dir_path ${j_project_file} DIRECTORY)
	get_filename_component(config_dir_name ${config_dir_path} NAME)

	if ("${j_project_name}" STREQUAL "")
		set(j_project_name "${config_dir_name}" )
	endif()

	if ("${j_project_root}" STREQUAL "")
		set(j_project_root ${config_dir_path} )
	endif()
	get_filename_component(parent_dir_of_project_root ${j_project_root} DIRECTORY)

	if ("${j_sibling_root_dir}" STREQUAL "")
		set(j_sibling_root_dir ${parent_dir_of_project_root} )
	endif()

	if ("${j_config_dir}" STREQUAL "")
		if ("${j_config}" STREQUAL "Debug")
			set(j_config_dir "debug" )
		elseif ("${j_config}" STREQUAL "Release")
			set(j_config_dir "release" )
		else()
			j_assert("Unsupported config: ${j_config}" FALSE)
		endif()
	endif()
	if ("${j_output_root_dir}" STREQUAL "")
		set (j_output_root_dir ${parent_dir_of_project_root}/Toolkit )
	endif()

	if ("${j_project_output_root_dir}" STREQUAL "" AND NOT "${j_project_name}" STREQUAL "")
		set (j_project_output_root_dir ${j_output_root_dir}/${j_project_name} )
	endif()

	if ("${j_generated_project_dir}" STREQUAL "")
		set(j_generated_project_dir ${j_project_output_root_dir}/build/${j_platform}/${j_toolset}_${j_toolset_ver}/${j_architecture}/${j_config_dir} )
	endif()

	if ("${j_jenny_3rd_cache}" STREQUAL "")
		j_set_path(j_jenny_3rd_cache ${j_pkg_local_root}/cache)
	endif()
	if ("${j_jenny_3rd_libs}" STREQUAL "")
		j_set_path(j_jenny_3rd_libs ${j_pkg_local_root}/libs)
	endif()
	if ("${j_jenny_tmp}" STREQUAL "")
		j_set_path(j_jenny_tmp ${j_pkg_local_root}/tmp)
	endif()

endmacro()

function (j_print_global_settings)
	foreach(var IN LISTS j_global_variables)
		message(STATUS "${var}=${${var}}")
	endforeach()
endfunction()

function (j_load_scripts path_pattern)
	file(GLOB scripts  FOLLOW_SYMLINKS "${path_pattern}")
	list(SORT scripts)
	foreach(src IN LISTS scripts)
		include(${src})
	endforeach()
endfunction()



function (j_sync_package libname url local sub_dir extract_dir)
	if (NOT EXISTS ${local})
		message("Downloading ${local} from ${url}...")
		if ("${j_server_user_passwd}" STREQUAL "")
			file(DOWNLOAD ${url} ${local} STATUS status )
		else()
			file(DOWNLOAD ${url} ${local} USERPWD ${j_server_user_passwd} STATUS status )
		endif()
		list(GET status 0 result)
		if (NOT result EQUAL 0)
			file(REMOVE ${local})
			message(FATAL_ERROR "Failed to download ${libname} package from ${url}. Error status: ${status}")
		endif()
	endif()
	if (EXISTS ${local} AND NOT IS_DIRECTORY ${local} AND NOT EXISTS ${extract_dir})
		message("Extract ${local}... to ${extract_dir}")
		file(REMOVE_RECURSE ${j_jenny_tmp})
		file(MAKE_DIRECTORY ${j_jenny_tmp})

		execute_process( COMMAND ${CMAKE_COMMAND} -E tar xf ${local}
			WORKING_DIRECTORY ${j_jenny_tmp}
			)
		get_filename_component(extract_parent_dir ${extract_dir} DIRECTORY)
		file(MAKE_DIRECTORY ${extract_parent_dir})
		message("RENAME ${j_jenny_tmp}/${sub_dir} ${extract_dir}")
		file(RENAME ${j_jenny_tmp}/${sub_dir} ${extract_dir})
		if (EXISTS ${j_jenny_tmp}/${sub_dir} OR NOT EXISTS ${extract_dir})
			message(FATAL_ERROR "Failed to move directory ${j_jenny_tmp}/${sub_dir} to ${extract_dir}")
		endif()
		file(REMOVE_RECURSE ${j_jenny_tmp})
	endif()

endfunction()


#sync 3rd party libraries to local cache.
#it will call external tools to get files.
function (j_sync_libraries )
	set(has_error false)

	set(pkg_list "_pkg_header" "_pkg_lib" "_pkg_bin" "_pkg_sym")
	set(dest_list "_include_dir" "_lib_dir" "_bin_dir" "_sym_dir")
	set(length 3)
	foreach(lib IN LISTS j_3rd_list)
		if (j_skip_sync_${lib} OR "$ENV{J_NO_PACKAGE_DOWNLOAD}")
			message("  --\tSkip lib ${lib}, remote ${j_lib_${lib}_remote_path}")
		else()
			message ("Get downloading library ${lib}...${j_3rd_server}/${j_lib_${lib}_remote_path}")
			set(server "")
			if ("${j_lib_${lib}_server}" STREQUAL "")
				set(server "${j_pkg_server}")
			else()
				set(server "${j_lib_${lib}_server}")
			endif()
			if ("${j_lib_${lib}_server_root}" STREQUAL "")
				set(server "${server}${j_pkg_server_root}")
			else()
				set(server "${server}${j_lib_${lib}_server_root}")
			endif()

			set(url_base ${server}/${j_lib_${lib}_remote_path})

			foreach(idx RANGE ${length})
				list(GET pkg_list ${idx} pkg_post)
				list(GET dest_list ${idx} dest_post)
				set(pkg_file ${j_lib_${lib}${pkg_post}})
				set(dest_dir ${j_lib_${lib}${dest_post}})
				set(sub_dir ${j_lib_${lib}${pkg_post}_from_dir})	
				if (NOT "${pkg_file}" STREQUAL "")
					if ("${j_lib_${lib}_type}" STREQUAL "zip")
						j_sync_package("${lib}" "${url_base}/${pkg_file}" "${j_jenny_3rd_cache}/${pkg_file}" "${sub_dir}" "${dest_dir}" )
					else()		# for nuget
						execute_process(COMMAND nuget  install "${pkg_file}" 
							-version ${j_lib_${lib}_version}
							-configfile "${j_jenny_root}/../NuGet.config"
							-outputdirectory "${j_jenny_3rd_libs}" RESULT_VARIABLE result)
						if (NOT result EQUAL 0)
							set(has_error false)
							message(${j_sync_error_mode} "Failed to get project ${proj} with path ${j_external_project_${proj}_url}.")
						endif()
					endif()
				endif()
			endforeach()
		endif()
	endforeach()
	if (has_error)
		message(FATAL_ERROR "Failed to fetch third party libraries, stop processing.")
	endif()
endfunction()

function (j_sync_external_projects)
	set(has_error false)

	foreach(proj IN LISTS j_external_project_list)
		
		set(result 0)
		if (j_skip_sync_${proj} OR "$ENV{J_NO_PROJECT_DOWNLOAD}")
			message("  --\tSkip downloading project ${proj}, url ${j_external_project_${proj}_url}")
		else()
			message ("Get Project  ${proj}... ${j_external_project_${proj}_url}")
			if (j_verbose)
				execute_process(COMMAND "${j_cmd_sync_project}" "${j_external_project_${proj}_path}" "${j_external_project_${proj}_url}"
					RESULT_VARIABLE result
				)
			else()
				execute_process(COMMAND "${j_cmd_sync_project}" "${j_external_project_${proj}_path}" "${j_external_project_${proj}_url}" 
					RESULT_VARIABLE result
					OUTPUT_VARIABLE output
					ERROR_VARIABLE outerr
				)
			endif()
		endif()

		if (NOT result EQUAL 0)
			set(has_error false)
			message(${j_sync_error_mode} "Failed to get project ${proj} with path ${j_external_project_${proj}_url}.")
			if (DEFINED output)
				message("\n  Output: ${output}" 
						"\n  Out Err: ${outerr}")
			endif()
		endif()
	endforeach()
	if (has_error)
		message(FATAL_ERROR "Failed to sync external projects, stop processing.")
	endif()


endfunction()

# options:
# j_add_3rd( <libname> <version> )
function (j_add_3rd libname version) # [path in remote]
	j_assert("lib name can't be empty" NOT libname STREQUAL  j_empty )
	j_assert("lib version can't be empty" NOT version STREQUAL  j_empty)
	
	set(pkg_lib_name ${libname})
	set(pkg_config ${j_pkg_config})
	set(pkg_arch ${j_pkg_arch})
	set(pkg_extension ${j_pkg_extension})
	set(pkg_version ${version})
	set(pkg_server ${j_pkg_server})
	set(pkg_server_root ${j_pkg_server_root})
	set(pkg_type ${j_pkg_type})

	set (keywords NAME SERVER  SERVER_ROOT OS  CONFIG ARCH COMPILER FAKE_VERSION  PKG_EXTENSION PKG_HEADER PKG_BIN PKG_LIB PKG_SYM STATIC)
	list(LENGTH ARGN argc)
	if (argc GREATER 0)
		Math(EXPR argc "${argc}-1")
		foreach(idx RANGE 0 ${argc} 2)
			list(GET ARGN ${idx} keyword)
			Math(EXPR idx "${idx}+1")
			list(GET ARGN ${idx} value)

			if ("${keyword}" STREQUAL "SERVER")
				set(pkg_server "${value}")
			elseif ("${keyword}" STREQUAL "SERVER_ROOT")
				set(pkg_server_root "${value}")
			elseif ("${keyword}" STREQUAL "NAME")
				set(pkg_lib_name "${value}")
			elseif ("${keyword}" STREQUAL "OS")
				set(pkg_os "${value}")
			elseif ("${keyword}" STREQUAL "CONFIG")
				set(pkg_config "${value}")
			elseif ("${keyword}" STREQUAL "ARCH")
				set(pkg_arch "${value}")
			elseif ("${keyword}" STREQUAL "COMPILER")
				set(pkg_compiler "${value}")
			elseif ("${keyword}" STREQUAL "FAKE_VERSION")
				set(pkg_version "${value}")
			elseif ("${keyword}" STREQUAL "PKG_EXTENSION")
				set(pkg_extension "${value}")
			elseif ("${keyword}" STREQUAL "PKG_HEADER")
				set(pkg_header "${value}")
			elseif ("${keyword}" STREQUAL "PKG_BIN")
				set(pkg_bin "${value}")
			elseif ("${keyword}" STREQUAL "PKG_LIB")
				set(pkg_lib "${value}")
			elseif ("${keyword}" STREQUAL "PKG_SYM")
				set(pkg_sym "${value}")
			elseif ("${keyword}" STREQUAL "STATIC")
				set(pkg_is_static "${value}")
			elseif ("${keyword}" STREQUAL "TYPE")
				set(pkg_type "${value}")
			elseif ("${keyword}" STREQUAL "GITURL")
				set(pkg_git_url "${value}")
			else()
				j_assert("Unsupport keyword ${keyword}" FALSE)
			endif()
		endforeach()
	endif()
	if ("${pkg_type}" STREQUAL "nuget")
		set(pkg_extension "")
	endif()
	if (NOT DEFINED pkg_os)
		if ("${j_pkg_os}" STREQUAL "mac" AND "${pkg_type}" STREQUAL "nuget")
			set(pkg_os "osx")
		else()
			set(pkg_os ${j_pkg_os})
		endif()
	endif()
	if (NOT DEFINED pkg_compiler)
		if ("${pkg_type}" STREQUAL "nuget")
			string(REGEX REPLACE "^vc" "v" pkg_compiler ${j_pkg_compiler})
		else()
			set(pkg_compiler ${j_pkg_compiler})
		endif()
	endif()

	if (NOT "${pkg_os}" STREQUAL "")
		set(name_body _${pkg_os})
		set(recognized_path "${pkg_os}/")
	endif()
	if (NOT "${pkg_config}" STREQUAL "")
		set(name_body ${name_body}_${pkg_config})
	endif()
	if (NOT "${pkg_arch}" STREQUAL "")
		set(name_body ${name_body}_${pkg_arch})
	endif()
	if (NOT "${pkg_compiler}" STREQUAL "")
		set(name_body ${name_body}_${pkg_compiler})
	endif()
	if (NOT "${pkg_version}" STREQUAL "" AND "${pkg_type}" STREQUAL "zip")
		set(name_body ${name_body}.${pkg_version})
	endif()
	set(name_body ${name_body}${pkg_extension})

	if (NOT "${pkg_compiler}" STREQUAL "")
		set(recognized_path "${recognized_path}${pkg_compiler}/")
	endif()
	if (NOT "${pkg_arch}" STREQUAL "")
		set(recognized_path "${recognized_path}${pkg_arch}/")
	endif()
	if (NOT "${pkg_config}" STREQUAL "")
		set(recognized_path "${recognized_path}${pkg_config}")
	endif()
	if (NOT DEFINED pkg_git_url)
		if ("${pkg_type}" STREQUAL "git")
			set(pkg_type "nuget")
		endif()
	endif()


	set(lib_list ${j_3rd_list})
	list(APPEND lib_list ${libname})
	set(j_3rd_list ${lib_list} PARENT_SCOPE)

	set(j_lib_${libname}_type ${pkg_type} PARENT_SCOPE)
	set(j_lib_${libname}_name ${pkg_lib_name} PARENT_SCOPE)
	set(j_lib_${libname}_version ${version} PARENT_SCOPE)
	set(j_lib_${libname}_remote_path ${pkg_lib_name}/${version} PARENT_SCOPE)
	set(j_lib_${libname}_server ${pkg_server} PARENT_SCOPE)
	set(j_lib_${libname}_server_root ${pkg_server_root} PARENT_SCOPE)
	set(j_lib_${libname}_git_url ${pkg_git_url} PARENT_SCOPE)
	set(lib_short_path ${pkg_lib_name}/${version})
	set(lib_local_path ${j_jenny_3rd_libs}/${pkg_lib_name}/${version})

	set(j_lib_${libname}_short_path ${lib_short_path} PARENT_SCOPE)
	set(j_lib_${libname}_local_path ${lib_local_path} PARENT_SCOPE)

	if (NOT DEFINED pkg_header)
		if("${pkg_type}" STREQUAL "nuget")
			set(pkg_header ${pkg_lib_name}-headers_${pkg_os})
		else()
			set(pkg_header ${pkg_lib_name}-headers.${pkg_version}${pkg_extension})
		endif()
	endif()
	set(j_lib_${libname}_pkg_header ${pkg_header} PARENT_SCOPE)
	set(j_lib_${libname}_pkg_header_from_dir "include" PARENT_SCOPE)

	if (NOT DEFINED pkg_lib)
		set(pkg_lib ${pkg_lib_name}-lib${name_body})
	endif()
	set(j_lib_${libname}_pkg_lib ${pkg_lib} PARENT_SCOPE)
	set(j_lib_${libname}_pkg_lib_from_dir "lib" PARENT_SCOPE)

	if (NOT DEFINED pkg_bin)
		set(pkg_bin ${pkg_lib_name}${name_body})
	endif()
	set(j_lib_${libname}_pkg_bin ${pkg_bin} PARENT_SCOPE)
	set(j_lib_${libname}_pkg_bin_from_dir "bin" PARENT_SCOPE)

	if (NOT DEFINED pkg_sym)
		set(pkg_sym ${pkg_lib_name}-sym${name_body})
	endif()
	set(j_lib_${libname}_pkg_sym ${pkg_sym} PARENT_SCOPE)
	set(j_lib_${libname}_pkg_sym_from_dir "bin" PARENT_SCOPE)


	if("${pkg_type}" STREQUAL "git")
		j_add_external_project(${libname} ${pkg_git_url})
	elseif("${pkg_type}" STREQUAL "zip")
		set(j_lib_${libname}_include_dir  "${j_jenny_3rd_libs}/${lib_short_path}/include" PARENT_SCOPE)
		set(j_lib_${libname}_lib_dir  "${j_jenny_3rd_libs}/${lib_short_path}/lib/${recognized_path}" PARENT_SCOPE)
		set(j_lib_${libname}_bin_dir  "${j_jenny_3rd_libs}/${lib_short_path}/bin/${recognized_path}" PARENT_SCOPE)
		set(j_lib_${libname}_sym_dir  "${j_jenny_3rd_libs}/${lib_short_path}/sym/${recognized_path}" PARENT_SCOPE)
		set(j_lib_${libname}_src_dir  "${j_jenny_3rd_libs}/${lib_short_path}/src" PARENT_SCOPE)
		if (WIN32 OR pkg_is_static)
			set(j_lib_${libname}_default_link_dir  "${j_jenny_3rd_libs}/${lib_short_path}/lib/${recognized_path}" PARENT_SCOPE)
		else()
			set(j_lib_${libname}_default_link_dir  "${j_jenny_3rd_libs}/${lib_short_path}/bin/${recognized_path}" PARENT_SCOPE)
		endif()
	elseif("${pkg_type}" STREQUAL "nuget")
		set(j_lib_${libname}_include_dir  "${j_jenny_3rd_libs}/${pkg_header}.${pkg_version}/inc" PARENT_SCOPE)

		set(j_lib_${libname}_lib_dir  "${j_jenny_3rd_libs}/${pkg_lib}.${pkg_version}/libs" PARENT_SCOPE)
		set(j_lib_${libname}_bin_dir  "${j_jenny_3rd_libs}/${pkg_bin}.${pkg_version}/bin" PARENT_SCOPE)
		set(j_lib_${libname}_sym_dir  "${j_jenny_3rd_libs}/${pkg_sym}.${pkg_version}/bin" PARENT_SCOPE)
		if (WIN32 OR pkg_is_static)
			set(j_lib_${libname}_default_link_dir  "${j_jenny_3rd_libs}/${pkg_lib}.${pkg_version}/libs" PARENT_SCOPE)
		else()
			set(j_lib_${libname}_default_link_dir  "${j_jenny_3rd_libs}/${pkg_bin}.${pkg_version}/bin" PARENT_SCOPE)
		endif()
	else()
		j_assert("Unknown library type ${}")
	endif()

	set(j_lib_${libname}_variables 
		"j_lib_${libname}_type" 
		"j_lib_${libname}_name" 
		"j_lib_${libname}_version" 
		"j_lib_${libname}_server" 
		"j_lib_${libname}_server_root" 
		"j_lib_${libname}_git_url" 
		"j_lib_${libname}_short_path" 
		"j_lib_${libname}_local_path" 
		"j_lib_${libname}_pkg_header" 
		"j_lib_${libname}_pkg_lib" 
		"j_lib_${libname}_pkg_bin" 
		"j_lib_${libname}_pkg_sym" 

		"j_lib_${libname}_include_dir" 
		"j_lib_${libname}_lib_dir" 
		"j_lib_${libname}_bin_dir" 
		"j_lib_${libname}_src_dir" 
		"j_lib_${libname}_default_link_dir" 
		PARENT_SCOPE)

	if("${pkg_type}" STREQUAL "nuget")
		set(j_external_project_${libname}_runtime_path "${project_output_root_dir}/bin/${j_recognized_path}" PARENT_SCOPE)
		set(j_external_project_${libname}_library_path "${j_jenny_3rd_libs}/${pkg_lib}.${pkg_version}/bin" PARENT_SCOPE)
		set(j_external_project_${libname}_archive_path "${j_jenny_3rd_libs}/${pkg_bin}.${pkg_version}/libs" PARENT_SCOPE)
		set(j_external_project_${libname}_sym_path "${j_jenny_3rd_libs}/${pkg_sym}.${pkg_version}/bin" PARENT_SCOPE)
		if (WIN32)
			set(j_external_project_${libname}_link_path "${j_jenny_3rd_libs}/${pkg_lib}.${pkg_version}/libs" PARENT_SCOPE)
		else()
			set(j_external_project_${libname}_link_path "${j_jenny_3rd_libs}/${pkg_bin}.${pkg_version}/bin" PARENT_SCOPE)
		endif()
	endif()
endfunction()

macro (j_add_lib libname version)
	if ("$ENV{J_GIT_FOR_LIBRARY}")
		set(args "TYPE;zip")
	else()
		set(args "TYPE;nuget")
	endif()

	set(extra_args "${ARGN}") # wrong behavior if pass ${ARGN} as argument directly
	if (NOT "${extra_args}" STREQUAL "")
		list(APPEND args "${extra_args}")
	endif()

	j_add_3rd(${libname} ${version} "${args}")
endmacro()

function (j_add_external_project proj url)
	j_assert("project name can't be empty" NOT proj STREQUAL  j_empty )
	j_assert("project url can't be empty" NOT url STREQUAL  j_empty )

	list(APPEND j_external_project_list ${proj})
	list(APPEND j_external_project_url_list ${url})

	set(j_external_project_list ${j_external_project_list} PARENT_SCOPE)
	set(j_external_project_url_list ${j_external_project_url_list} PARENT_SCOPE)

	set(sibling_dir ${j_sibling_root_dir}/${proj})
	set(j_external_project_${proj}_path ${sibling_dir} PARENT_SCOPE)
	set(j_external_project_${proj}_url ${url} PARENT_SCOPE)

	set(project_output_root_dir ${j_output_root_dir}/${proj})
	set(j_external_project_${proj}_runtime_path "${project_output_root_dir}/bin/${j_recognized_path}" PARENT_SCOPE)
	set(j_external_project_${proj}_library_path "${project_output_root_dir}/bin/${j_recognized_path}" PARENT_SCOPE)
	set(j_external_project_${proj}_archive_path "${project_output_root_dir}/lib/${j_recognized_path}" PARENT_SCOPE)
	set(j_external_project_${proj}_sym_path "${project_output_root_dir}/sym/${j_recognized_path}" PARENT_SCOPE)
	if (WIN32)
		set(j_external_project_${proj}_link_path "${project_output_root_dir}/lib/${j_recognized_path}" PARENT_SCOPE)
	else()
		set(j_external_project_${proj}_link_path "${project_output_root_dir}/bin/${j_recognized_path}" PARENT_SCOPE)
    endif()
endfunction()

# set or change the property for given library
# sometimes we may need to change the 3rd config by switching some options. 
# this would be a useful helper function
function (j_set_3rd_property libname keyword)
	message(FATAL_ERROR "Not implemented")
endfunction()

function (j_add_projects dir bin_dir)
	file(GLOB_RECURSE projects FOLLOW_SYMLINKS ${dir}/CMakeLists.txt)
	foreach(proj IN LISTS projects)
		get_filename_component(proj_dir "${proj}" PATH)
		file(RELATIVE_PATH rpath ${dir} ${proj_dir})
		add_subdirectory(${proj_dir} ${bin_dir}/${rpath})
	endforeach()
endfunction()

#debug only, show info of defined 3rd library

function (j_show_3rd_libinfo lib)
	message("Lib: " ${lib})
	foreach(var IN LISTS j_lib_${lib}_variables)
		message("  -- ${var}: ${${var}}")
	endforeach()
endfunction()
function (j_show_list_libs)
	foreach(lib IN LISTS j_3rd_list)
		j_show_3rd_libinfo(${lib})
	endforeach()
endfunction()

function (j_group_source files file_root project_root group_name)
	foreach(src IN LISTS files)
		j_assert ("src should be absoult file path" IS_ABSOLUTE  ${src})
		# remove leading file_root part and file name part
		file(RELATIVE_PATH group_path "${file_root}" "${src}")
		get_filename_component(group_path "${group_path}" PATH)
		string (REPLACE "/" "\\" group_path "${group_path}")

		if (group_path STREQUAL "")
			set (group_path "${group_name}")
		else()
			set (group_path "${group_name}\\${group_path}")
		endif()

		source_group( "${group_path}" FILES "${src}")
	endforeach()
endfunction()

function (j_add_source output dir)
	set (keywords SOURCE HEADER GENERIC SOURCE_GROUP HEADER_GROUP GENERIC_GROUP)
	set(source_group_name "Source Files")
	set(header_group_name "Header Files")
	set(general_group_name "Other Files")

	list(LENGTH ARGN argc)
	if (argc EQUAL 0)
		set(header_exts "${dir}/*.h;${dir}/*.hpp;${dir}/*.hxx;${dir}/*.hh")
		set(source_exts "${dir}/*.cpp;${dir}/*.cxx;${dir}/*.c;${dir}/*.cc")
		set(general_exts "${dir}/*?.*")

	else()		
		foreach(ext IN LISTS ARGN)
			list(FIND keywords ${ext} index)
			if (NOT index EQUAL -1)
				set(current_keyword ${ext})
			else()
				if (current_keyword STREQUAL SOURCE)
					list(APPEND source_exts "${dir}/${ext}")
				elseif(current_keyword STREQUAL HEADER)
					list(APPEND header_exts "${dir}/${ext}")
				elseif(current_keyword STREQUAL GENERIC)
					list(APPEND general_exts "${dir}/${ext}")
				elseif(current_keyword STREQUAL SOURCE_GROUP)
					set(source_group_name "${ext}")
				elseif(current_keyword STREQUAL HEADER_GROUP)
					set(header_group_name "${ext}")
				elseif(current_keyword STREQUAL GENERIC_GROUP)
					set(general_group_name "${ext}")
				else()
					j_assert("Keyword should be one of ${keywords}" FALSE)
				endif()
			endif()
		endforeach()
	endif()

    list(LENGTH general_exts size)
    set(files3 "")
    if (NOT size EQUAL 0)
        file(GLOB_RECURSE  files3 FOLLOW_SYMLINKS ${general_exts})
    endif()

	set (project_dir "${CMAKE_BINARY_DIR}/Project")
	list(LENGTH header_exts size)
	if (NOT size EQUAL 0)
		file(GLOB_RECURSE  files1 FOLLOW_SYMLINKS ${header_exts})
        list(REMOVE_ITEM  files3  "" ${files1})
		j_group_source("${files1}" "${dir}" "${project_dir}" "${header_group_name}")
	endif()

	list(LENGTH source_exts size)
	if (NOT size EQUAL 0)
		file(GLOB_RECURSE  files2 FOLLOW_SYMLINKS ${source_exts})
        list(REMOVE_ITEM  files3  "" ${files2} )
		j_group_source("${files2}" "${dir}" "${project_dir}" "${source_group_name}")
	endif()

	list(LENGTH files3 other_files_count)
	if (NOT other_files_count EQUAL 0)
		j_group_source("${files3}" "${dir}" "${project_dir}" "${general_group_name}")
	endif()

	set(${output} "${files1};${files2};${files3}" PARENT_SCOPE)
endfunction()

function(j_aux_add_source output)
	j_add_source(files ${J_PROJECT_SOURCE_DIR})
	set(${output} ${files} PARENT_SCOPE)
endfunction()

function(j_project project )
	project(${project})

	if (DEFINED ARGV1)
		j_set_path(source_dir ${j_project_root}/${ARGV1} )
		message(STATUS "Project: ${project}")
	endif()
	set(J_PROJECT_SOURCE_DIR ${source_dir} PARENT_SCOPE)
endfunction()

function (j_add_tools exe)
	j_add_source(files ${J_PROJECT_SOURCE_DIR}/${exe})

	add_executable(${exe} ${files})
	if (DEFINED ARGN)
		target_link_libraries( ${exe} LINK_PRIVATE ${ARGN})
	endif()
	if (DEFINED TOOLS_COMMON_DEPENDENCY)
		add_dependencies(${exe} ${TOOLS_COMMON_DEPENDENCY})
	endif()

	if (DEFINED WARN_AS_ERROR_OFF)
		set_target_properties(${WARN_AS_ERROR_OFF} PROPERTIES COMPILE_FLAGS "/WX-")
	endif()

	j_set_target_default(${exe})
	if (NOT DEFINED XR_TOOLS_FOLDER)
		set(XR_TOOLS_FOLDER Tools)
	endif()
	set_property(TARGET ${exe} PROPERTY FOLDER ${XR_TOOLS_FOLDER})
endfunction()

function (j_set_target_default target)
	get_target_property(type ${target} TYPE)
	if (type STREQUAL EXECUTABLE)
		set (path_for_runtime ${j_default_deploy_property_path})
		set (path_for_copy ${j_default_runtime_path})
	else()
		set (path_for_runtime ${j_default_runtime_property_path})
		set (path_for_copy ${j_default_deploy_path})
	endif()

	set_target_properties(${target} PROPERTIES
		ARCHIVE_OUTPUT_DIRECTORY ${j_default_archive_property_path}
		LIBRARY_OUTPUT_DIRECTORY ${j_default_library_property_path}
		RUNTIME_OUTPUT_DIRECTORY ${path_for_runtime}
		PDB_OUTPUT_DIRECTORY 	 ${j_default_sym_property_path})

    if(APPLE)
        set_target_properties(${target} PROPERTIES
            XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf-with-dsym"
            XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS YES
			XCODE_ATTRIBUTE_DYLIB_COMPATIBILITY_VERSION "${j_current_version}"
			XCODE_ATTRIBUTE_DYLIB_CURRENT_VERSION "${j_compatibility_version}"
        )
    endif()

	if(MSVC)
		set_property(TARGET ${target} APPEND PROPERTY LINK_FLAGS /DEBUG)
	else()
		#already set 
	endif()

	if (NOT type STREQUAL STATIC_LIBRARY)
		if(XCODE)
			add_custom_command(TARGET ${target} POST_BUILD
				COMMAND ${CMAKE_COMMAND} -E copy_directory "$<TARGET_FILE:${target}>.dSYM" "${j_default_sym_path}/$<TARGET_FILE_NAME:${target}>.dSYM")
			set_target_properties(${target} PROPERTIES
				BUILD_WITH_INSTALL_RPATH TRUE
				INSTALL_NAME_DIR "@loader_path"
			)
		endif()
		j_copy_target(${target} ${path_for_copy})
	endif()

	if (NOT ARGV1)
		#j_set_visibility_hidden(${target})
	endif()
endfunction()

function(j_copyfile target dest)
    add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${dest} )
    foreach(file IN LISTS ARGN)
        add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${file} ${dest} )
    endforeach()
endfunction()
function(j_copydir target dest src)
    add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${dest} )
    foreach(dir IN LISTS ARGN)
		add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${src}/${dir} ${dest}/${dir} )
    endforeach()
endfunction()

function(j_copyframework target src dest)
    if(APPLE)
        add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${dest} )
        foreach(framework IN LISTS ARGN)
			add_custom_command(TARGET ${target} POST_BUILD
				COMMAND rsync -a --delete ${src}/${framework}/ ${dest}/${framework}
				COMMENT "copy ${framework}")
		endforeach()
	endif()
endfunction()


function(j_set_visibility_hidden target)
    if(APPLE)
        set_target_properties(${target} PROPERTIES
            XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN YES
            XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES
        )
    endif()

endfunction()

function(j_search_path var from_path check_path)
	if(DEFINED ARGV3)
		set(checked_path ${ARGV3})
	else()
		set(checked_path ${check_path})
	endif()
	
	if (DEFINED ${${from_path}})
		file(TO_CMAKE_PATH ${${from_path}} current_dir)
	else()
		file(TO_CMAKE_PATH ${CMAKE_HOME_DIRECTORY} current_dir)
	endif()

	get_filename_component(current_dir ${current_dir} DIRECTORY)

	while (NOT EXISTS ${current_dir}/${check_path})
		set(old_dir ${current_dir})
		get_filename_component(current_dir ${current_dir}  DIRECTORY)
		if (${current_dir} STREQUAL ${old_dir} )
			break ()
		endif()
	endwhile() 

	if (EXISTS ${current_dir}/${check_path})
		set( ${var} ${current_dir}/${checked_path} PARENT_SCOPE)
	else()
		set( ${var} "" PARENT_SCOPE)
	endif()
endfunction()

function(j_gen_version_file from_file to_file)
	if ("${from_file}" IS_NEWER_THAN "${to_file}")
		file(STRINGS "${from_file}" contents)
		list(LENGTH contents length)
		file(WRITE ${to_file} "//Generated by Jenny...\n" )
		foreach(line IN LISTS contents)
			string(FIND "${line}" "=" idx)
			if (NOT ${idx} EQUAL "-1") 
				string(SUBSTRING "${line}" 0 ${idx} item)
				string(STRIP ${item} item)
				Math(EXPR idx "${idx}+1")
				string(SUBSTRING "${line}" ${idx} -1 value)
				string(STRIP ${value} value)
				file(APPEND ${to_file} "#define ${item} ${value}\n")
			endif()
		endforeach()
	endif()
endfunction()

function(j_gen_version_cs_file from_file to_file)
	if ("${from_file}" IS_NEWER_THAN "${to_file}")
		file(STRINGS "${from_file}" contents)
		list(LENGTH contents length)
		file(WRITE ${to_file} "//Generated by Jenny...\n" )
		file(APPEND ${to_file} "using System.Reflection;\n")
		file(APPEND ${to_file} "#if DEBUG\n")
		file(APPEND ${to_file} "\t[assembly: AssemblyConfiguration(\"NonProduction\")]\n")	
		file(APPEND ${to_file} "#else\n")
		file(APPEND ${to_file} "\t[assembly: AssemblyConfiguration(\"Production\")]\n\n")
		file(APPEND ${to_file} "#endif\n")	
		file(APPEND ${to_file} "[assembly: AssemblyTrademark(\"\")]\n")
		file(APPEND ${to_file} "[assembly: AssemblyCulture(\"\")]\n")
		foreach(line IN LISTS contents)
			string(FIND "${line}" "=" idx)
			if (NOT ${idx} EQUAL "-1") 
				string(SUBSTRING "${line}" 0 ${idx} item)
				string(STRIP ${item} item)
				Math(EXPR idx "${idx}+1")
				string(SUBSTRING "${line}" ${idx} -1 value)
				string(STRIP ${value} value)
				if("${item}" STREQUAL "AD_VERSION_STR")
					file(APPEND ${to_file} "[assembly: AssemblyFileVersion(${value})]\n")
					file(APPEND ${to_file} "#if !USES_PRIVATE_ASSEMBLY_VERSION\n")
					file(APPEND ${to_file} "[assembly: AssemblyVersion(${value})]\n")
					file(APPEND ${to_file} "#endif\n")
				elseif("${item}" STREQUAL "AD_COMPANYNAME")
					file(APPEND ${to_file} "[assembly: AssemblyCompany(${value})]\n")
				elseif("${item}" STREQUAL "AD_PRODUCTNAME")
					file(APPEND ${to_file} "[assembly: AssemblyProduct(${value})]\n")
				elseif("${item}" STREQUAL "AD_COPYRIGHT_NOTICE")
					file(APPEND ${to_file} "[assembly: AssemblyCopyright(${value})]\n")
				else()
					#do nothing
				endif()
			endif()
		endforeach()
	endif()
endfunction()


function(j_gen_version_wxi_file from_file to_file)
	if ("${from_file}" IS_NEWER_THAN "${to_file}")
		file(STRINGS "${from_file}" contents)
		list(LENGTH contents length)
		file(WRITE ${to_file} "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
		file(APPEND ${to_file} "<Include>\n")
		foreach(line IN LISTS contents)
			string(FIND "${line}" "=" idx)
			if (NOT ${idx} EQUAL "-1") 
				string(SUBSTRING "${line}" 0 ${idx} item)
				string(STRIP ${item} item)
				Math(EXPR idx "${idx}+1")
				string(SUBSTRING "${line}" ${idx} -1 value)
				string(STRIP ${value} value)
				if("${item}" STREQUAL "AD_VERSION_STR")
					file(APPEND ${to_file} "<?define ProductVersion=${value} ?>\n")
				else()
					#do nothing
				endif()
			endif()
		endforeach()
		file(APPEND ${to_file} "</Include>\n")
	endif()
endfunction()


# j_copy_target target [dest_path   [ext]]
function(j_copy_target target_name dest_path)
	add_custom_command(TARGET ${target_name} POST_BUILD
		COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${target_name}>" "${dest_path}/$<TARGET_FILE_NAME:${target_name}>")
endfunction()

function (j_add_custome_source result cmd option src_dir dest_dir in_ext out_ext in_group_name out_group_name prefix)
	
	set (project_dir "${CMAKE_BINARY_DIR}/Project")
	file(GLOB_RECURSE  srcfiles FOLLOW_SYMLINKS "${src_dir}/*${in_ext}")
	
    j_group_source("${srcfiles}" "${src_dir}" "${project_dir}" "${in_group_name}")

	set(generated_files "")
	foreach(filep IN LISTS srcfiles)
		get_filename_component(file_path "${filep}" REALPATH)
		get_filename_component(file_dir "${file_path}" DIRECTORY)
		get_filename_component(file_name "${file_path}" NAME_WE)
		set(output_file_path "${dest_dir}/${prefix}${file_name}${out_ext}")
		add_custom_command(OUTPUT ${output_file_path}
					   COMMAND "${cmd}" "${file_path}" "${option}" "${output_file_path}"
					   MAIN_DEPENDENCY ${filep}
					   DEPENDS ${filep}
					   COMMENT "Generate ${output_file_path}")
		list(APPEND generated_files ${output_file_path})

	endforeach()
	set_source_files_properties(${generated_files} PROPERTIES GENERATED TRUE)
	j_group_source("${generated_files}" "${dest_dir}" "${project_dir}" "${out_group_name}")
	set(${result} "${srcfiles};${generated_files}" PARENT_SCOPE)
endfunction()

function (j_add_moc_source result src_dir sub_folder)

	j_set_path(generated_path "${j_default_generated_path}/${sub_folder}")
    if(NOT EXISTS generated_path)
        file(MAKE_DIRECTORY ${generated_path})
    endif()
	j_add_custome_source(files "${j_lib_qt_cmd_moc}" "-o" "${src_dir}" "${generated_path}" ".h" ".cpp" "Moc Files" "Moc Files" "moc_")
	set(${result} "${files}" PARENT_SCOPE)
endfunction()

function (j_add_ui_source result src_dir  sub_folder)
	j_set_path(generated_path "${j_default_generated_path}/${sub_folder}")
	if(NOT EXISTS generated_path)
		file(MAKE_DIRECTORY ${generated_path})
	endif()
	j_add_custome_source(files "${j_lib_qt_cmd_uic}" "-o" "${src_dir}" "${generated_path}" ".ui" ".h" "UI Files" "UI Files" "ui_")
	set(${result} "${files}" PARENT_SCOPE)
endfunction()


function (j_add_qm_source target_name translation_src_dir ts_file_list)
   file(GLOB locales RELATIVE ${translation_src_dir} ${translation_src_dir}/*)
   foreach(locale_dir ${locales})
      if(IS_DIRECTORY ${translation_src_dir}/${locale_dir})
         j_set_path(generate_qm_path "${j_default_runtime_property_path}/${j_config}/${locale_dir}")
         if(NOT EXISTS generate_qm_path)
            file(MAKE_DIRECTORY ${generate_qm_path})
         endif()
         foreach(ts_file ${ts_file_list})
            add_custom_command(TARGET ${target_name} PRE_BUILD
            COMMAND ${j_lib_qt_cmd_lrelease} -silent ${translation_src_dir}/${locale_dir}/${ts_file}.ts -qm ${generate_qm_path}/${ts_file}.qm)
         endforeach()
      endif()
   endforeach()
endfunction()

function(j_add_xlf target_name translation_src_dir xlf_file_list)
   file(GLOB locales RELATIVE ${translation_src_dir} ${translation_src_dir}/*)
   foreach(locale_dir ${locales})
      if(IS_DIRECTORY ${translation_src_dir}/${locale_dir})
         j_set_path(generate_locale_path "${j_default_runtime_property_path}/${j_config}/${locale_dir}")
         if(NOT EXISTS generate_locale_path)
            file(MAKE_DIRECTORY ${generate_locale_path})
         endif()
         foreach(xlf_file ${xlf_file_list})			
			add_custom_command(TARGET ${target_name} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${translation_src_dir}/${locale_dir}/${xlf_file}.xlf ${generate_locale_path} )
         endforeach()
      endif()
   endforeach()
endfunction()

function (j_copy_qmfile target src dest)
       file(GLOB locales RELATIVE ${src} ${src}/*)
       foreach(locale_dir ${locales})
               if(IS_DIRECTORY ${src}/${locale_dir})
                       add_custom_command(TARGET ${target} POST_BUILD
                       COMMAND ${CMAKE_COMMAND} -E make_directory ${dest}/${locale_dir})
            add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${src}/${locale_dir} ${dest}/${locale_dir})

            #j_copyfile(${target} ${dest}/${locale_dir} "${qm_files}")
               endif()
       endforeach()
endfunction()

function (j_add_rcc_source result src_dir  sub_folder)
       j_set_path(generated_path "${j_default_generated_path}/${sub_folder}")
    if(APPLE)
        if(NOT EXISTS generated_path)
            file(MAKE_DIRECTORY ${generated_path})
        endif()
    endif()
       j_add_custome_source(files "${j_lib_qt_cmd_rcc}" "-o" "${src_dir}" "${generated_path}" ".qrc" ".cpp" "Resource Files" "Resource Files" "qrc_")
       set(${result} "${files}" PARENT_SCOPE)
endfunction()

